package com.ss.project.xia17user.config.security;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import com.ss.project.xia17user.config.security.auth2.CustomAuth2AuthorizationEndpointFilter;
import com.ss.project.xia17user.config.security.auth2.UserConsentPage;
import com.ss.project.xia17user.config.security.form.FormAuthenticationFilter;
import com.ss.project.xia17user.config.security.jose.Jwks;
import com.ss.project.xia17user.config.sys.SysConfigManager;
import com.ss.project.xia17user.service.ClientService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.InMemoryOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;

/**
 * security 配置
 * @author xia17
 * @date 2020/6/8
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();

    private final ClientService clientService;
    private final UserDetailsServiceImpl userDetailsService;
    private final SysConfigManager sysConfigManager;
    private final RegisteredClientRepository registeredClientRepository;
    private final UserConsentPage userConsentPage;

    /**
     * 配置url 拦截
     * @param http HttpSecurity
     * @throws Exception 异常
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception{
        // 下面这段代码部分抄自 OAuth2AuthorizationServerConfiguration.class
        OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
                new OAuth2AuthorizationServerConfigurer<>();

        // 采用oauth2 server的自动配置
        http.apply(authorizationServerConfigurer);

        http
                .csrf().disable()
                .cors().disable()
                // 添加自定义的表单登录认证
                .addFilterBefore(new FormAuthenticationFilter(sysConfigManager,userDetailsService,authenticationManagerBean()) , UsernamePasswordAuthenticationFilter.class)
                // 添加自定义的Oauth2认证Filter
                .addFilterBefore(new CustomAuth2AuthorizationEndpointFilter(registeredClientRepository,oAuth2AuthorizationService(),clientService,userConsentPage),
                        AbstractPreAuthenticatedProcessingFilter.class)
                // 采用表单登录
                .formLogin()
                // 登录页面
                .loginPage("/login")
                .and()
                // 启用auth2Server
                .oauth2ResourceServer()
                // oauth2的token使用jwt
                .jwt()
                .and().and()
                .exceptionHandling()
                .accessDeniedHandler(new CustomAccessDeniedHandler())
                .and();

        // 资源保护
        http.authorizeRequests()
                .antMatchers("/login","/error/**").permitAll()
                .antMatchers("/css/**","/img/**","/js/**").permitAll()
                .antMatchers("/test/**").permitAll()
                .anyRequest().authenticated();
    }


    /**
     * oauth2需要
     * @return /
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = Jwks.generateRsa();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }

    /**
     * oauth2需要
     * @return /
     */
    @Bean
    public ProviderSettings providerSettings() {
        return new ProviderSettings().issuer("http://auth-server:9000");
    }

    @Bean
    public OAuth2AuthorizationService oAuth2AuthorizationService(){
        return new InMemoryOAuth2AuthorizationService();
    }




}
